<?php
if (!defined('BASEPATH'))
    exit('No direct script access allowed');

class Meta_m extends Base_model {
	
	public $primary_key = 'meta_id';
	public $_table = "postmetas";
	public $_id_column = "post_id";
	
	function __construct() {
		parent::__construct();
	}
	
	/**
	 * Add metadata for the specified object.
	 *
	 * @since 1.0
	 *
	 * @param string $meta_type Type of object metadata is for (e.g., comment, post, or user)
	 * @param int $object_id ID of the object metadata is for
	 * @param string $meta_key Metadata key
	 * @param mixed $meta_value Metadata value. Must be serializable if non-scalar.
	 * @param bool $unique Optional, default is false. Whether the specified metadata key should be
	 *              unique for the object. If true, and the object already has a value for the specified
	 *              metadata key, no change will be made
	 * @return int|bool The meta ID on success, false on failure.
	 */
	function add_metadata($meta_type, $object_id, $meta_key, $meta_value, $unique = false) {
		
		if ( ! $meta_type || ! $meta_key || ! is_numeric( $object_id ) ) {
			return false;
		}

		$object_id = absint( $object_id );
		if ( ! $object_id ) {
			return false;
		}

		$table = $this->_get_meta_table( $meta_type );
		if ( ! $table ) {
			return false;
		}

		$column = sanitize_key($meta_type . '_id');

		// expected_slashed ($meta_key)
		$meta_key = wp_unslash($meta_key);
		$meta_value = wp_unslash($meta_value);
		
		
		if ( $unique && $this->get_by(array("meta_key" => $meta_key, "$column" => $object_id)) )
			return false;
		
		$_meta_value = $meta_value;
		$meta_value = maybe_serialize( $meta_value );
		
		$data = array(
			$column => $object_id,
			'meta_key' => $meta_key,
			'meta_value' => $meta_value
		);
		
		return $this->insert( $data, true );
	}
	
	/**
	 * Update metadata for the specified object. If no value already exists for the specified object
	 * ID and metadata key, the metadata will be added.
	 *
	 * @since 1.0
	 *
	 * @param string $meta_type Type of object metadata is for (e.g., comment, post, or user)
	 * @param int $object_id ID of the object metadata is for
	 * @param string $meta_key Metadata key
	 * @param mixed $meta_value Metadata value. Must be serializable if non-scalar.
	 * @param mixed $prev_value Optional. If specified, only update existing metadata entries with
	 * 		the specified value. Otherwise, update all entries.
	 * @return int|bool Meta ID if the key didn't exist, true on successful update, false on failure.
	 */
	function update_metadata($meta_type, $object_id, $meta_key, $meta_value, $prev_value = '') {
	
		if ( ! $meta_type || ! $meta_key || ! is_numeric( $object_id ) ) {
			return false;
		}

		$object_id = absint( $object_id );
		if ( ! $object_id ) {
			return false;
		}

		$table = $this->_get_meta_table( $meta_type );
		if ( ! $table ) {
			return false;
		}

		$column = sanitize_key($meta_type . '_id');

		// expected_slashed ($meta_key)
		$meta_key = wp_unslash($meta_key);
		$passed_value = $meta_value;
		$meta_value = wp_unslash($meta_value);
		$meta_value = maybe_serialize( $meta_value );
		
		$where = array();
		if($object_id)
			$where[$column] = $object_id;
		if($meta_key)
			$where['meta_key'] = $meta_key;
		
		if(!$row = $this->get_by($where))
			return $this->add_metadata($meta_type, $object_id, $meta_key, $passed_value);
		$this->reset_query();
		
		return $this->update($row->meta_id, array('meta_value' => $meta_value), true);
	}
	
	/**
	 * Delete metadata for the specified object.
	 *
	 * @since 1.0
	 *
	 * @param string $meta_type Type of object metadata is for (e.g., comment, post, or user)
	 * @param int $object_id ID of the object metadata is for
	 * @param string $meta_key Metadata key
	 * @param mixed $meta_value Optional. Metadata value. Must be serializable if non-scalar. If specified, only delete metadata entries
	 * 		with this value. Otherwise, delete all entries with the specified meta_key.
	 * @param bool $delete_all Optional, default is false. If true, delete matching metadata entries
	 * 		for all objects, ignoring the specified object_id. Otherwise, only delete matching
	 * 		metadata entries for the specified object_id.
	 * @return bool True on successful delete, false on failure.
	 */
	function delete_metadata($meta_type, $object_id, $meta_key, $meta_value = '', $delete_all = false) {
		// check
		if ( ! $meta_type || ! $meta_key || ! is_numeric( $object_id ) && ! $delete_all ) {
			return false;
		}
		
		$object_id = absint( $object_id );
		if ( ! $object_id && ! $delete_all ) {
			return false;
		}

		$table = $this->_get_meta_table( $meta_type );
		if ( ! $table ) {
			return false;
		}
		
		//format
		$type_column = sanitize_key($meta_type . '_id');
		// expected_slashed ($meta_key)
		$meta_key = wp_unslash($meta_key);
		$meta_value = wp_unslash($meta_value);
		
		//process
		
		$_meta_value = $meta_value;
		$meta_value = maybe_serialize( $meta_value );
		
		if ( !$delete_all )
			$this->where($type_column, $object_id );
		if ( $meta_value )
			$this->where("meta_value", $meta_value );
		if ( $meta_key )
			$this->where("meta_key", $meta_key );
		
		return $this->delete_many_by();
	}
	
	/**
	 * Retrieve metadata for the specified object.
	 *
	 * @since 1.0
	 *
	 * @param string $meta_type Type of object metadata is for (e.g., comment, post, or user)
	 * @param int $object_id ID of the object metadata is for
	 * @param string $meta_key Optional. Metadata key. If not specified, retrieve all metadata for
	 * 		the specified object.
	 * @param bool $single Optional, default is false. If true, return only the first value of the
	 * 		specified meta_key. This parameter has no effect if meta_key is not specified.
	 * @return string|array Single metadata value, or array of values
	 */
	function get_metadata($meta_type, $object_id, $meta_key = '', $single = false) {
		if ( ! $meta_type || ! is_numeric( $object_id ) ) {
			return false;
		}

		$object_id = absint( $object_id );
		if ( ! $object_id ) {
			return false;
		}
		$type_column = sanitize_key($meta_type . '_id');
		
		
		$where = array();
		$where[$this->_id_column] = $object_id;
		if(!empty($meta_key))
			$where['meta_key'] = $meta_key;
		
		$result = array();
		
		if($single)
			$this->select('meta_value');
		
		$result = $this->get_many_by($where); 
		
		$this->reset_query();
		if($single)
			return maybe_unserialize(@$result[0]->meta_value);
		else
			return $this->array_map_recursive_meta('maybe_unserialize', $result);
		
	}
	
	
	function array_map_recursive_meta($callback, $object) {
		foreach ($object as $key => $value) {
			$object[$key]->meta_value = call_user_func($callback, $object[$key]->meta_value);
		}
		return $object;
	}
	
	/**
	 * Retrieve the name of the metadata table for the specified object type.
	 *
	 * @since 1.0
	 *
	 * @param string $type Type of object to get metadata table for (e.g., comment, post, or user)
	 * @return mixed Metadata table name, or false if no metadata table exists
	 */
	function _get_meta_table($type = '') {

		$table_name = sanitize_key($type . 'meta');

		$result = $this->table_exists($table_name);
		if($result) {
			$this->_table = $table_name;
			
			$this->primary_key = 'user' == $type ? 'umeta_id' : 'meta_id';
			$this->_id_column = sanitize_key($type . '_id');
		}
		return $result;
	}
}

?>